﻿
using System.AuthCenter.DBModels;
using System.Security.Cryptography;

using GCC.Container.Abstracts;

using GCCGrpc.AuthCenter;

namespace System.AuthCenter
{
    /// <summary>
    /// 认证中心的管理器
    /// </summary>
    internal static class AuthManager
    {
        /// <summary>
        /// 每次启动得AESkey不同,每次的token加密则发生改变
        /// 如果不想拥有这个保护机制,把key与VI设置成固定值即可,比如,这个值在GCCEntrance进行初始化 Key与VI是可以赋值的.
        /// </summary>
        private static readonly Aes _aes = Aes.Create();
        /// <summary>
        /// 所有的角色缓存
        /// </summary>
        internal static readonly List<Roles> _roles = new();
        /// <summary>
        /// 所有的访问区域缓存
        /// </summary>
        internal static readonly List<Scopes> _scopes = new();



        /// <summary>
        /// 根据Token获取用户信息
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static GCCUserInfo? GetUserInfoByToken(AbsGCCContext context, GetUserByTokenRequest request)
        {
            var currentUIDStr = DecryptStringFromBytes_Aes(request.AccessToken);//这句是用来防止用户使用上次的token(系统重启前)来欺骗系统,例如一个长期运行的token要想关闭此功能,注释这句话的同时将_aes得key与IV固定
#if DEBUG
            if (request.Token == "testUserToken")
            {
                currentUIDStr = "123";
            }
#endif

            if (string.IsNullOrWhiteSpace(currentUIDStr) || !long.TryParse(currentUIDStr, out var _))
            {
                return null;
            }
            var currentUserInfo = context.TryGet<GCCUserInfo>(request.AccessToken).Result;
            if (currentUserInfo is null)
            {
                return null;
            }
            var userInfo = context.TryGet<GCCUserInfo>(request.Token).Result;

            return userInfo;
        }

        /// <summary>
        /// 登陆
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="request">请求</param>
        /// <param name="token">登陆成功后的token</param>
        /// <returns></returns>
        public static GCCUserInfo? Login(AbsGCCContext context, LoginRequest request, out string? token)
        {
            token = null;
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalReadDB is null)
            {
                return null;
            }
            if (string.IsNullOrWhiteSpace(request.OpenKey))
            {
                return null;
            }

            var where = GCCEntrance.DBManager.GlobalReadDB.Select<UserLoginInfos>()
                .Where(item => item.Type == request.Type)
                .WhereIf(!string.IsNullOrWhiteSpace(request.OpenID), item => item.OpenID == request.OpenID)
                .WhereIf(!string.IsNullOrWhiteSpace(request.OpenKey), item => item.OpenKey == request.OpenKey)
                .WhereIf(!string.IsNullOrWhiteSpace(request.RefreshToken), item => item.RefreshToken == request.RefreshToken)
                .WhereIf(!string.IsNullOrWhiteSpace(request.Token), item => item.Token == request.Token)
                .WhereIf(!string.IsNullOrWhiteSpace(request.UnionID), item => item.UnionID == request.UnionID)
                .IncludeMany(item => item.UserRoles)
                .IncludeMany(item => item.UserScopes)
                ;



            if (where.Count() != 1)
            {
                return null;
            }

            var dbRes = where.First();
            if (dbRes is null)
            {
                return null;
            }
            token = EncryptStringToBytes_Aes($"{dbRes.UID}");
            if (token is null)
            {
                return null;
            }
            var rolesID = dbRes.UserRoles.Select(item => item.RoleID).ToList();
            var scopesID = dbRes.UserScopes.Select(item => item.ScopeID).ToList();
            var userInfo = new GCCUserInfo()
            {
                UID = dbRes.UID,
                Roles = _roles.Where(item => rolesID.Contains(item.RoleID)).Select(item => item.Name).ToList(),
                Scopes = _scopes.Where(item => scopesID.Contains(item.ScopeID)).Select(item => item.Name).ToList()
            };
            if (request.CacheSecond != 0)
            {
                context.Set<GCCUserInfo>(token, userInfo, new CacheItemSettings()
                {
                    SaveToAll = true,
                    SlidingExpiration = TimeSpan.FromSeconds(request.CacheSecond),
                }).Wait();
            }
            else if (request.CacheToSeconds != 0)
            {
                context.Set<GCCUserInfo>(token, userInfo, new CacheItemSettings()
                {
                    SaveToAll = true,
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(request.CacheToSeconds)
                }).Wait();
            }

            return userInfo;
        }

        public static void Logout(AbsGCCContext context, string token)
        {
            _ = context.Remove(token);
        }

        /// <summary>
        /// 登陆一个测试用户
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="token">登陆成功的token</param>
        /// <returns></returns>
        internal static GCCUserInfo? LoginTestUser(AbsGCCContext context, out string token)
        {
            token = "testUserToken";
            var userInfo = new GCCUserInfo()
            {
                UID = -1,
                Roles = new List<string>() { "*" },
                Scopes = new List<string>() { "*" },
            };
            context.Set<GCCUserInfo>(token, userInfo, new CacheItemSettings()
            {
                SaveToAll = true,
            }).Wait();

            return userInfo;
        }

        /// <summary>
        /// 新增用户,如果存在,则是更新
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static bool SetUser(SetUserRequest request)
        {
            var currentUIDStr = DecryptStringFromBytes_Aes(request.AccessToken);
            if (string.IsNullOrWhiteSpace(currentUIDStr) || !long.TryParse(currentUIDStr, out var currentUID))
            {
                return false;
            }
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalDB is null)
            {
                return false;
            }
            if (request == null)
            {
                return false;
            }

            var login = new UserLoginInfos()
            {
                OpenID = request.OpenID,
                OpenKey = request.OpenKey,
                RefreshToken = request.RefreshToken,
                Token = request.Token,
                Type = request.Type,
                UID = request.UID,
                UnionID = request.UnionID,
                CreatedBy = currentUID,
                ModifiedBy = currentUID
            };
            var dbres = GCCEntrance.DBManager.GlobalDB.InsertOrUpdate<UserLoginInfos>().SetSource(login).ExecuteAffrows();

            return dbres == 1;
        }

        /// <summary>
        /// 获取用户的所有登陆信息
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static List<UserLoginInfos> GetUserLoginInfos(GetUserLoginInfosRequest request)
        {
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalReadDB is null)
            {
                return new();
            }
            if (request == null)
            {
                return new();
            }
            return GCCEntrance.DBManager.GlobalReadDB.Select<UserLoginInfos>().Where(item => item.UID == request.UID).ToList();
        }

        /// <summary>
        /// 获取用户的所有角色
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static List<Roles> GetUserRoles(GetUserRolesRequest request)
        {
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalReadDB is null)
            {
                return new();
            }
            if (request == null)
            {
                return new();
            }
            var allUserRoles = GCCEntrance.DBManager.GlobalReadDB.Select<UserRoles>()
                .Where(item => item.UID == request.UID).ToList();
            var allRoleIDs = allUserRoles.Select(item => item.RoleID).ToList();
            return _roles.Where(item => allRoleIDs.Contains(item.RoleID)).ToList();
        }

        /// <summary>
        /// 设置用户的角色
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static bool SetUserRoles(SetUserRolesRequest request)
        {
            var currentUIDStr = DecryptStringFromBytes_Aes(request.AccessToken);
            if (string.IsNullOrWhiteSpace(currentUIDStr) || !long.TryParse(currentUIDStr, out var currentUID))
            {
                return false;
            }
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalDB is null)
            {
                return false;
            }
            if (request == null)
            {
                return false;
            }
            var res = false;
            GCCEntrance.DBManager.GlobalDB.Transaction(() =>
            {
                _ = GCCEntrance.DBManager.GlobalDB.Delete<UserRoles>()
                .Where(item => item.UID == request.UID).ExecuteAffrows();
                var items = new List<UserRoles>();
                foreach (var item in request.RoleID)
                {
                    items.Add(new UserRoles()
                    {
                        UID = request.UID,
                        RoleID = item,
                        CreatedBy = currentUID,
                        ModifiedBy = currentUID
                    });
                }
                if (GCCEntrance.DBManager.GlobalDB.Insert(items).ExecuteAffrows() != request.RoleID.Count)
                {
                    throw new Exception("");
                }
                res = true;
            });


            return res;
        }

        /// <summary>
        /// 获取用户的所有访问范围
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static List<Scopes> GetUserScopes(GetUserScopesRequest request)
        {
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalReadDB is null)
            {
                return new();
            }
            if (request == null)
            {
                return new();
            }
            var allUserScopes = GCCEntrance.DBManager.GlobalReadDB.Select<UserScopes>()
                .Where(item => item.UID == request.UID).ToList();
            var allScopesID = allUserScopes.Select(item => item.ScopeID).ToList();
            return _scopes.Where(item => allScopesID.Contains(item.ScopeID)).ToList();
        }
        /// <summary>
        /// 设置用户的访问范围
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static bool SetUserScopes(SetUserScopesRequest request)
        {
            var currentUIDStr = DecryptStringFromBytes_Aes(request.AccessToken);
            if (string.IsNullOrWhiteSpace(currentUIDStr) || !long.TryParse(currentUIDStr, out var currentUID))
            {
                return false;
            }
            if (GCCEntrance.DBManager is null || GCCEntrance.DBManager.GlobalDB is null)
            {
                return false;
            }
            if (request == null)
            {
                return false;
            }
            var res = false;
            GCCEntrance.DBManager.GlobalDB.Transaction(() =>
            {
                _ = GCCEntrance.DBManager.GlobalDB.Delete<UserScopes>()
                .Where(item => item.UID == request.UID).ExecuteAffrows();
                var items = new List<UserScopes>();
                foreach (var item in request.ScopesID)
                {
                    items.Add(new UserScopes()
                    {
                        UID = request.UID,
                        ScopeID = item,
                        CreatedBy = currentUID,
                        ModifiedBy = currentUID
                    });
                }
                if (GCCEntrance.DBManager.GlobalDB.Insert(items).ExecuteAffrows() != request.ScopesID.Count)
                {
                    throw new Exception("");
                }
                res = true;
            });


            return res;
        }


        /// <summary>
        /// 加密一个字符串
        /// </summary>
        /// <param name="plainText">原始文字</param>
        /// <returns>加密结果</returns>
        private static string? EncryptStringToBytes_Aes(string plainText)
        {
            if (string.IsNullOrWhiteSpace(plainText))
            {
                return null;
            }
            try
            {
                byte[] encrypted;
                using Aes aesAlg = Aes.Create();

                aesAlg.Key = _aes.Key;
                aesAlg.IV = _aes.IV;

                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                using MemoryStream msEncrypt = new();
                using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
                using StreamWriter swEncrypt = new(csEncrypt);
                swEncrypt.Write(plainText);
                swEncrypt.Flush();
                swEncrypt.Close();
                encrypted = msEncrypt.ToArray();



                return BitConverter.ToString(encrypted).Replace("-", "");
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 解密一个字符串
        /// </summary>
        /// <param name="cipherText">原始文字</param>
        /// <returns>解密结果</returns>
        private static string? DecryptStringFromBytes_Aes(string cipherText)
        {
            if (string.IsNullOrWhiteSpace(cipherText))
            {
                return null;
            }

            try
            {

                byte[] buf1 = new byte[cipherText.Length / 2];
                for (int i = 0; i < cipherText.Length; i += 2)
                {
                    buf1[i / 2] = Convert.ToByte(cipherText[i..(i + 2)], 16);
                }
                using Aes aesAlg = Aes.Create();
                aesAlg.Key = _aes.Key;
                aesAlg.IV = _aes.IV;
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using MemoryStream msDecrypt = new(buf1);
                using CryptoStream csDecrypt = new(msDecrypt, decryptor, CryptoStreamMode.Read);
                using StreamReader srDecrypt = new(csDecrypt);
                return srDecrypt.ReadToEnd();
            }
            catch
            {
                return null;
            }
        }
    }
}
